/**
 * 
 */
package cz.cuni.mff.abacs.burglar.visual.play_state;

import cz.cuni.mff.abacs.burglar.logics.GameMap;
import cz.cuni.mff.abacs.burglar.logics.objects.BaseInterface;
import cz.cuni.mff.abacs.burglar.logics.objects.agents.Agent;
import cz.cuni.mff.abacs.burglar.logics.objects.agents.BeliefBase;
import cz.cuni.mff.abacs.burglar.logics.objects.agents.Burglar;
import cz.cuni.mff.abacs.burglar.logics.objects.agents.Guard;
import cz.cuni.mff.abacs.burglar.logics.objects.positions.*;
import cz.cuni.mff.abacs.burglar.logics.planning.instructions.Instruction;
import cz.cuni.mff.abacs.burglar.visual.VisualBurglar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.newdawn.slick.*;
import org.newdawn.slick.geom.Polygon;
import org.newdawn.slick.geom.Shape;
import org.newdawn.slick.state.StateBasedGame;


/**
 * Contains the graphics representation of the {@link PlayState}.
 * 
 * @author abacs
 *
 */
public class GraphicPlayState extends InterfacedPlayState {
	// constants:
	
	
	private static int SCALE = 1;
	
	/** Filter representing well known objects. */
	private static Color COLOR_FILTER_KNOWN = Color.white;
	/** Filter representing not known objects. */
	private static Color COLOR_FILTER_UNKNOWN = Color.darkGray;
	/** Filter representing objects that are different than the agent thinks it is. */
	private static Color COLOR_FILTER_MISTAKEN = Color.lightGray;
	
	
	// -------------------------------------------------------------------------
	// properties:
	
	
	private Image _imgBurglar = null;
	private Image _imgBurglarDisguised = null;
	
	private Image _imgGuard = null;
	private Image _imgGuardNaked = null;
	private Image _imgGuardDead = null;
	
	private Image _imgContainerClosed = null;
	private Image _imgContainerOpened = null;
	private Image _imgContainerLocked = null;
	private Image _imgDoorClosed = null;
	private Image _imgDoorOpened = null;
	private Image _imgDoorLocked = null;
	private Image _imgFloor = null;
	private Image _imgPhone = null;
	private Image _imgCameraOn = null;
	private Image _imgCameraOff = null;
	private Image _imgSelection = null;
	private Image _imgSwitchOff = null;
	private Image _imgSwitchOn = null;
	private Image _imgVenderOff = null;
	private Image _imgVenderOn = null;
	private Image _imgWall = null;
	
	
	// -------------------------------------------------------------------------
	// constructors:
	
	
	/**
	 * 
	 * 
	 * @param stateID
	 * @throws SlickException
	 */
	public GraphicPlayState(int stateID)
			throws SlickException {
		super(stateID);
	}
	
	
	// -------------------------------------------------------------------------
	
	
	/** 
	 * Initialize any needed data before the game loop.
	 * 
	 * @param container
	 * @param game
	 */
	@Override
	public void init(GameContainer container, StateBasedGame game)
			throws SlickException {
		super.init(container, game);
		
		this._imgBurglar = new Image(VisualBurglar.PATH_IMAGES + "burglar.gif");
		this._imgBurglarDisguised = 
				new Image(VisualBurglar.PATH_IMAGES + "burglar_disguised.gif");
		
		this._imgGuard = new Image(VisualBurglar.PATH_IMAGES + "guard.gif");
		this._imgGuardNaked = 
				new Image(VisualBurglar.PATH_IMAGES + "guard_naked.gif");
		this._imgGuardDead = 
				new Image(VisualBurglar.PATH_IMAGES + "guard_dead.gif");
		
		this._imgContainerClosed = new Image(VisualBurglar.PATH_IMAGES + "container_closed.png");
		this._imgContainerLocked = new Image(VisualBurglar.PATH_IMAGES + "container_locked.png");
		this._imgContainerOpened = new Image(VisualBurglar.PATH_IMAGES + "container_opened.png");
		this._imgDoorClosed = new Image(VisualBurglar.PATH_IMAGES + "door_closed.gif");
		this._imgDoorOpened = new Image(VisualBurglar.PATH_IMAGES + "door_opened.png");
		this._imgDoorLocked = new Image(VisualBurglar.PATH_IMAGES + "door_locked.gif");
		this._imgFloor = new Image(VisualBurglar.PATH_IMAGES + "floor.gif");
		this._imgPhone = new Image(VisualBurglar.PATH_IMAGES + "phone.gif");
		this._imgCameraOn = new Image(VisualBurglar.PATH_IMAGES + "camera_on.gif");
		this._imgCameraOff = new Image(VisualBurglar.PATH_IMAGES + "camera_off.gif");
		this._imgSelection = new Image(VisualBurglar.PATH_IMAGES + "selection.gif");
		this._imgSwitchOff = new Image(VisualBurglar.PATH_IMAGES + "switch_off.gif");
		this._imgSwitchOn = new Image(VisualBurglar.PATH_IMAGES + "switch_on.gif");
		this._imgVenderOff = new Image(VisualBurglar.PATH_IMAGES + "vender_off.gif");
		this._imgVenderOn = new Image(VisualBurglar.PATH_IMAGES + "vender_on.gif");
		this._imgWall = new Image(VisualBurglar.PATH_IMAGES + "wall.gif");
	}
	
	
	/**
	 *  Allows to draw the world.
	 *  
	 *  @param container
	 *  @param game
	 *  @param graphics
	 *  @throws SlickException
	 */
	@Override
	public void render(
			GameContainer container,
			StateBasedGame game,
			Graphics graphics
	) throws SlickException {
		try{
			// draw the map:
			this.drawMap(graphics);
			// selection:
			this.drawSelection();
		}catch(Exception e){
			// invalid game map
			System.err.println(e.toString());
		}
		super.render(container, game, graphics);
	}
	
	
	// -------------------------------------------------------------------------
	
	
	/**
	 * Draws the whole map area.
	 * 
	 * @param graphics
	 */
	private void drawMap(Graphics graphics) {
		// draw wall:
		if(VisualBurglar.FLAG_SOLID_BACKGROUND == false){
			for(int x = 0; x < this._screen._width; x++){
				for(int y = 0; y < this._screen._height; y++){
					this._imgWall.draw(
							x * VisualBurglar.RESOURCE_BLOCK_SIZE,
							y * VisualBurglar.RESOURCE_BLOCK_SIZE,
							GraphicPlayState.SCALE
					);
				}
			}
		}else{
			graphics.setBackground(new org.newdawn.slick.Color(200,200,200));
		}
		
		
		
		if(this._map == null)
			return;
		
		List<Integer> trapRoomIds = this._map.getTrapRoomIds();
		
		// draw all the positions:
		for(Position pos : this._map.getPositions())
			this.drawPosition(pos, this._player.getSelectedAgent(), trapRoomIds, graphics);
		
		// TODO remove drawing duplication
		
		for(Position pos : this._map.getOperablePositions())
			this.drawPosition(pos, this._player.getSelectedAgent(), trapRoomIds, graphics);
		
		// draw the intent line:
		this.drawIntent(
				this._player.getSelectedAgent(),
				this._player.getIntentDistance(),
				// TODO temporal color change
				org.newdawn.slick.Color.black,//org.newdawn.slick.Color.red,
				graphics
		);
		
		for(Agent agent : this._map.getAgents())
			this.drawAgent(agent, this._player.getSelectedAgent());
	}
	
	
	/**
	 * 
	 * 
	 * @param position
	 * @param viewingAgent
	 * @return
	 */
	private static Color setColorFilter(Position position, Agent viewingAgent) {
		if(viewingAgent == null)
			return COLOR_FILTER_KNOWN;
		
		if(viewingAgent.getBeliefBase().isKnownPosition(position) == false)
			return COLOR_FILTER_UNKNOWN;
		
		if(viewingAgent.getBeliefBase().matches(position))
			return COLOR_FILTER_KNOWN;
		else
			return COLOR_FILTER_MISTAKEN;
	}
	
	
	/**
	 * Sets the color filter based on the viewingAgent's knowledge.
	 */
	private static Color setColorFilter(Agent agent, Agent viewingAgent) {
		if(viewingAgent == null || viewingAgent == agent)
			return COLOR_FILTER_KNOWN;
		
		if(viewingAgent.getBeliefBase().isKnownAgent(agent) == false)
			return COLOR_FILTER_UNKNOWN;
		
		if(viewingAgent.getBeliefBase().matches(agent))
			return COLOR_FILTER_KNOWN;
		else
			return COLOR_FILTER_MISTAKEN;
	}
	
	
	
	private boolean shouldDraw(Position position) {
		if(! this.isInScreen(position))
			return false;
		
		if(VisualBurglar.FLAG_PLAYER_CONTROL_MODE){
			BeliefBase beliefs = this._map.getBurglar().getBeliefBase();
			
			
			
		//	if(position.isTypeOf(BaseInterface.Type.DOOR)){
		//		int[] rooms = ((Door)position).getRoomIds();
		//		if(beliefs.isKnownRoom(rooms[0]) && beliefs.isKnownRoom(rooms[1]))
		//			return true;
		//	}else{
				if(beliefs.isKnownRoom(position.getRoomId()) || beliefs.isKnownPosition(position))
					return true;
		//	}
			return false;
		}
		
		return true;
	}
	
	
	/**
	 * Non-filtered means the burglar knows the position perfectly.
	 * 
	 * Light grey filter means the burglar knows the position, 
	 * but misses some detail.
	 * Dark grey filter means the burglar does not know the position at all.
	 * 
	 * @param position the position to draw
	 * @param viewingAgent the agent that's belief base is visualized
	 * @param trapRoomIds rooms that are observed by the guards
	 * @param graphics 
	 */
	private void drawPosition(
			Position position,
			Agent viewingAgent,
			List<Integer> trapRoomIds,
			Graphics graphics
	) {
		if(VisualBurglar.FLAG_PLAYER_CONTROL_MODE){
			this.drawRememberedPosition(position, viewingAgent, trapRoomIds, graphics);
		}else{
			this.drawObjectivePosition(position, viewingAgent, trapRoomIds, graphics);
		}
		
	}
	
	/**
	 * Non-filtered means the burglar knows the position perfectly.
	 * 
	 * Light grey filter means the burglar knows the position, 
	 * but misses some detail.
	 * Dark grey filter means the burglar does not know the position at all.
	 * 
	 * @param position the position to draw
	 * @param viewingAgent the agent that's belief base is visualized
	 * @param trapRoomIds rooms that are observed by the guards
	 * @param graphics 
	 */
	private void drawObjectivePosition(
			Position position,
			Agent viewingAgent,
			List<Integer> trapRoomIds,
			Graphics graphics
	) {
		
		if(this.shouldDraw(position) == false)
			return;
		
		int x = this._screen.mapXToScreenX(position.getX());
		int y = this._screen.mapYToScreenY(position.getY());
		
		// the filter indicates the whether 
		// the burglar agent knows the position
		Color filter = setColorFilter(position, viewingAgent);
		
		
		// draws a default floor background
		if(trapRoomIds.contains(position.getRoomId())){
			if(VisualBurglar.FLAG_SOLID_BACKGROUND == false){
				this._imgFloor.draw(x, y, GraphicPlayState.SCALE);
			}else{
				graphics.setColor(new Color(255, 255, 102));
				graphics.fillRect(
						x, y,
						VisualBurglar.RESOURCE_BLOCK_SIZE * GraphicPlayState.SCALE,
						VisualBurglar.RESOURCE_BLOCK_SIZE * GraphicPlayState.SCALE
				);
			}
		}else{
			if(VisualBurglar.FLAG_SOLID_BACKGROUND == false){
				this._imgFloor.draw(x, y, GraphicPlayState.SCALE, COLOR_FILTER_MISTAKEN);
			}else{
				graphics.setColor(COLOR_FILTER_MISTAKEN);
				graphics.fillRect(
						x, y,
						VisualBurglar.RESOURCE_BLOCK_SIZE * GraphicPlayState.SCALE,
						VisualBurglar.RESOURCE_BLOCK_SIZE * GraphicPlayState.SCALE
				);
			}
		}
		
		switch(position.getType()){
		case CAMERA:
			if(((Camera)position).isActive()){
				this._imgCameraOn.draw(x, y, GraphicPlayState.SCALE, filter);
			}else{
				this._imgCameraOff.draw(x, y, GraphicPlayState.SCALE, filter);
			}
			break;
		case CONTAINER:
			if(((Container)position).isClosed()){
				if(((Container)position).isLocked()){
					this._imgContainerLocked.draw(x, y, GraphicPlayState.SCALE, filter);
				}else{
					this._imgContainerClosed.draw(x, y, GraphicPlayState.SCALE, filter);
				}
			}else{
				this._imgContainerOpened.draw(x, y, GraphicPlayState.SCALE, filter);
			}
			break;
		case DOOR:
			
			if(((Door)position).isClosed()){
				if(((Door)position).isLocked())
					this._imgDoorLocked.draw(x, y - 16, GraphicPlayState.SCALE, filter);
				else
					this._imgDoorClosed.draw(x, y - 16, GraphicPlayState.SCALE, filter);
			}else{
				this._imgDoorOpened.draw(x, y - 16, GraphicPlayState.SCALE, filter);
			}
			break;
		case FLOOR:
			// floor background already drawn
			break;
		case PHONE:
			this._imgPhone.draw(x, y, GraphicPlayState.SCALE, filter);
			break;
		case SWITCH:
			if(((Switch)position).isActive())
				this._imgSwitchOn.draw(x, y, GraphicPlayState.SCALE, filter);
			else
				this._imgSwitchOff.draw(x, y, GraphicPlayState.SCALE, filter);
			break;
		case VENDER:
			if(((Vender)position).hasDropped())
				this._imgVenderOn.draw(x, y - 16, GraphicPlayState.SCALE, filter);
			else
				this._imgVenderOff.draw(x, y - 16, GraphicPlayState.SCALE, filter);
			break;
		default:
			
		}
	}
	
	
	/**
	 * Non-filtered means the burglar knows the position perfectly.
	 * 
	 * Light grey filter means the burglar knows the position, 
	 * but misses some detail.
	 * Dark grey filter means the burglar does not know the position at all.
	 * 
	 * @param position the position to draw
	 * @param viewingAgent the agent that's belief base is visualized
	 * @param trapRoomIds rooms that are observed by the guards
	 * @param graphics 
	 */
	private void drawRememberedPosition(
			Position position,
			Agent viewingAgent,
			List<Integer> trapRoomIds,
			Graphics graphics
	) {
		if(this.shouldDraw(position) == false)
			return;
		
		int x = this._screen.mapXToScreenX(position.getX());
		int y = this._screen.mapYToScreenY(position.getY());
		
		
		BeliefBase burglarBeliefs = this._map.getBurglar().getBeliefBase();
		
		boolean observedRoom = burglarBeliefs.isObservedRoom(position.getRoomId());
		
		if(observedRoom){
			if(VisualBurglar.FLAG_SOLID_BACKGROUND == false){
				this._imgFloor.draw(x, y, GraphicPlayState.SCALE);
			}else{
				graphics.setColor(new Color(255, 255, 102));
				graphics.fillRect(
						x, y,
						VisualBurglar.RESOURCE_BLOCK_SIZE * GraphicPlayState.SCALE,
						VisualBurglar.RESOURCE_BLOCK_SIZE * GraphicPlayState.SCALE
				);
			}
		}else{
			if(VisualBurglar.FLAG_SOLID_BACKGROUND == false){
				this._imgFloor.draw(x, y, GraphicPlayState.SCALE, COLOR_FILTER_MISTAKEN);
			}else{
				graphics.setColor(COLOR_FILTER_MISTAKEN);
				graphics.fillRect(
						x, y,
						VisualBurglar.RESOURCE_BLOCK_SIZE * GraphicPlayState.SCALE,
						VisualBurglar.RESOURCE_BLOCK_SIZE * GraphicPlayState.SCALE
				);
			}
		}
		
		if(position.isTypeOf(BaseInterface.Type.FLOOR))
			return;
		
		if(burglarBeliefs.isKnownPosition(position))
			position = burglarBeliefs.getKnownPosition(position.getId());
		Color filter = COLOR_FILTER_KNOWN;
		
		switch(position.getType()){
		case CAMERA:
			if(((Camera)position).isActive()){
				this._imgCameraOn.draw(x, y, GraphicPlayState.SCALE, filter);
			}else{
				this._imgCameraOff.draw(x, y, GraphicPlayState.SCALE, filter);
			}
			break;
		case CONTAINER:
			if(((Container)position).isClosed()){
				if(((Container)position).isLocked()){
					this._imgContainerLocked.draw(x, y, GraphicPlayState.SCALE, filter);
				}else{
					this._imgContainerClosed.draw(x, y, GraphicPlayState.SCALE, filter);
				}
			}else{
				this._imgContainerOpened.draw(x, y, GraphicPlayState.SCALE, filter);
			}
			break;
		case DOOR:
			
			if(((Door)position).isClosed()){
				if(((Door)position).isLocked())
					this._imgDoorLocked.draw(x, y - 16, GraphicPlayState.SCALE, filter);
				else
					this._imgDoorClosed.draw(x, y - 16, GraphicPlayState.SCALE, filter);
			}else{
				this._imgDoorOpened.draw(x, y - 16, GraphicPlayState.SCALE, filter);
			}
			break;
		case FLOOR:
			// floor background already drawn
			break;
		case PHONE:
			this._imgPhone.draw(x, y, GraphicPlayState.SCALE, filter);
			break;
		case SWITCH:
			if(((Switch)position).isActive())
				this._imgSwitchOn.draw(x, y, GraphicPlayState.SCALE, filter);
			else
				this._imgSwitchOff.draw(x, y, GraphicPlayState.SCALE, filter);
			break;
		case VENDER:
			if(((Vender)position).hasDropped())
				this._imgVenderOn.draw(x, y - 16, GraphicPlayState.SCALE, filter);
			else
				this._imgVenderOff.draw(x, y - 16, GraphicPlayState.SCALE, filter);
			break;
		default:
			
		}
	}
	
	
	/**
	 * Draws a single agent with possible filters.
	 * 
	 * @param agent
	 * @param viewingAgent the agent that's belief base is visualized
	 */
	private void drawAgent(Agent agent, Agent viewingAgent) {
		
		switch(agent.getType()){
		case BURGLAR:
			this.drawBurglar((Burglar)agent, viewingAgent);
			break;
		case DOG:
			// TODO dog
			break;
		case GUARD:
			this.drawGuard((Guard)agent, viewingAgent);
			break;
		default:
		}
	}
	
	
	/**
	 * Draws a burglar agent.
	 * 
	 * @param agent burglar agent
	 * @param viewingAgent the agent that's belief base is visualized
	 */
	private void drawBurglar(Burglar agent, Agent viewingAgent) {
		Position position = agent.getPosition();
		if(! this.isInScreen(position))
			return;
		
		int x = this._screen.mapXToScreenX(position.getX());
		int y = this._screen.mapYToScreenY(position.getY());
		
		Color filter = GraphicPlayState.setColorFilter(agent, viewingAgent);
		
		if(agent.isDisguised()){
			this._imgBurglarDisguised.draw(x, y - 16, GraphicPlayState.SCALE, filter);
		}else{
			this._imgBurglar.draw(x, y - 16, GraphicPlayState.SCALE, filter);
		}
	}
	
	
	/**
	 * Draws a guard agent.
	 * 
	 * @param agent
	 * @param viewingAgent the agent that's belief base is visualized
	 */
	private void drawGuard(Guard agent, Agent viewingAgent) {
		Position position = agent.getPosition();
		if(! this.isInScreen(position))
			return;
		
		if(VisualBurglar.FLAG_PLAYER_CONTROL_MODE){
			if(this._map.getBurglar().getBeliefBase().isKnownAgent(agent) == false){
				return;
			}
		}
		
		int x = this._screen.mapXToScreenX(position.getX());
		int y = this._screen.mapYToScreenY(position.getY());
		
		Color filter = GraphicPlayState.setColorFilter(agent, viewingAgent);
		
		switch(agent.getState()){
			case WELL:
				this._imgGuard.draw(x, y - 16, GraphicPlayState.SCALE, filter);
				break;
			case STUNNED:
				if(agent.hasItemOfType(BaseInterface.Type.UNIFORM)){
					this._imgGuardDead.draw(x - 4, y, GraphicPlayState.SCALE, filter);
				}else{
					this._imgGuardNaked.draw(x - 4, y, GraphicPlayState.SCALE, filter);
				}
				break;
		}
	}
	
	
	/**
	 * Agent intent line to draw.
	 * 
	 * @param agent 
	 * @param distance number of steps to show. if less than 0, shows all of them.
	 * @param lineColor
	 * @param graphics 
	 */
	private void drawIntent(
			Agent agent,
			int distance,
			Color lineColor,
			Graphics graphics
	) {
		if(agent == null)
			return;
		
		Position agentPos = agent.getPosition();
		
		Map<Position, String> stringsByPosition = new HashMap<Position, String>();
		
		int counter = 0;
		for(Instruction instruction : agent.getInstructions()){
			
			if(counter++ == distance)
				break;
			
			graphics.setColor(lineColor);
			
			Position subjectPos = this._map.getPosition(instruction._subjectId);
			
			switch(instruction._code){
			case MOVE:
				this.drawIntentMovement(agentPos, subjectPos, graphics);
				agentPos = subjectPos;
				break;
			case OPEN:
				this.appendToIntentString(stringsByPosition, subjectPos, STR_OPEN);
				break;
			case CLOSE:
				this.appendToIntentString(stringsByPosition, subjectPos, STR_CLOSE);
				break;
			case UNLOCK:
				this.appendToIntentString(stringsByPosition, subjectPos, STR_UNLOCK);
				break;
			case LOCK:
				this.appendToIntentString(stringsByPosition, subjectPos, STR_LOCK);
				break;
			case PICK_UP:
				this.appendToIntentString(stringsByPosition, subjectPos, STR_PICK_UP);
				break;
			case TAKE_CLOTHES:
				this.appendToIntentString(stringsByPosition, subjectPos, STR_TAKE_UNIFORM);
				break;
			case USE:
				this.appendToIntentString(stringsByPosition, subjectPos, STR_USE);
			default:
			}
		}
		
		// draw all strings gathered by positions:
		for(Entry<Position, String> entry : stringsByPosition.entrySet()){
			this.drawIntentStr(entry.getValue(), entry.getKey(), graphics);
		}
	}
	
	
	private void appendToIntentString(
			Map<Position, String> stringsByPosition,
			Position position,
			String toAppend
	) {
		if(stringsByPosition.containsKey(position)){
			String newString = stringsByPosition.get(position) + ", " + toAppend; 
			stringsByPosition.put(position, newString);
		}else{
			stringsByPosition.put(position, toAppend);
		}
	}
	
	
	/**
	 * Draws an intent string to a selected position.
	 * 
	 * Uses the previously selected line color.
	 * 
	 * @param text 
	 * @param pos game position to draw the text
	 * @param graphics
	 */
	private void drawIntentStr(String text, Position pos, Graphics graphics) {
		
		if(this.isInScreen(pos)){
			int x = this._screen.mapXToScreenX(pos.getX());
			int y = this._screen.mapYToScreenY(pos.getY());
			graphics.drawString(text, x, y);
		}
	}
	
	
	/**
	 * Draws movement intent from one position to another.
	 * 
	 * @param agentPos
	 * @param aimPos
	 * @param graphics 
	 */
	private void drawIntentMovement( Position agentPos, Position aimPos, Graphics graphics) {
		if(
			this.isInScreen(agentPos) &&
			this.isInScreen(aimPos)
		){
			int blockSize = VisualBurglar.RESOURCE_BLOCK_SIZE;
			
			int deltaX = blockSize / 2;
			int deltaY = blockSize / 2;
			
			int x1 = this._screen.mapXToScreenX(agentPos.getX()) + deltaX;
			int y1 = this._screen.mapYToScreenY(agentPos.getY()) + deltaY;
			int x2 = this._screen.mapXToScreenX(aimPos.getX()) + deltaX;
			int y2 = this._screen.mapYToScreenY(aimPos.getY()) + deltaY;
			
			int circleSize = blockSize / 8;
			graphics.drawOval(
					x2 - circleSize,
					y2 - circleSize,
					circleSize * 2,
					circleSize * 2
			);
			
			deltaX += circleSize;
			deltaY += circleSize;
			
			switch(this._map.getDirection(agentPos, aimPos)){
				case SOUTH_EAST:
					drawArrow(
							x1 + deltaX,
							y1 + deltaY,
							circleSize,
							GameMap.Direction.SOUTH_EAST,
							graphics
					);
					break;
				case EAST:
					drawArrow(
							x1 + deltaX,
							y1,
							circleSize,
							GameMap.Direction.EAST,
							graphics
					);
					break;
				case NORTH_EAST:
					drawArrow(
							x1 + deltaX,
							y1 - deltaY,
							circleSize,
							GameMap.Direction.NORTH_EAST,
							graphics
					);
					break;
				case SOUTH:
					drawArrow(
							x1,
							y1 + deltaY,
							circleSize,
							GameMap.Direction.SOUTH,
							graphics
					);
					break;
				case NORTH:
					drawArrow(
							x1,
							y1 - deltaY,
							circleSize,
							GameMap.Direction.NORTH,
							graphics
					);
					break;
				case SOUTH_WEST:
					drawArrow(
							x1 - deltaX,
							y1 + deltaY,
							circleSize,
							GameMap.Direction.SOUTH_WEST,
							graphics
					);
					break;
				case WEST:
					drawArrow(
							x1 - deltaX,
							y1,
							circleSize,
							GameMap.Direction.WEST,
							graphics
					);
					break;
				case NORTH_WEST:
					drawArrow(
							x1 - deltaX,
							y1 - deltaY,
							circleSize,
							GameMap.Direction.NORTH_WEST,
							graphics
					);
					break;
			}
					
			graphics.drawLine(x1, y1, x2, y2);			
		}
	}
	
	
	/**
	 * Draws an arrow on the coordinates x,y with the selected orientation.
	 * 
	 * Uses the previously selected line color.
	 * 
	 * @param x x coordinate in the window
	 * @param y y coordinate in the window
	 * @param length half length of the rectangle that contains the arrowhead
	 * @param direction direction of the arrow
	 * @param graphics
	 */
	private static void drawArrow(
			int x,
			int y,
			int halfLength,
			GameMap.Direction direction,
			Graphics graphics
	) {
		float[] points = null;
		switch(direction){
		case EAST:
			points = new float[]{
				x + halfLength, y,
				x - halfLength, y + halfLength,
				x - halfLength, y - halfLength,
			};
			break;
		case NORTH:
			points = new float[]{
				x,          y - halfLength,
				x + halfLength, y + halfLength,
				x - halfLength, y + halfLength,
			};
			break;
		case WEST:
			points = new float[]{
				x - halfLength, y,
				x + halfLength, y - halfLength,
				x + halfLength, y + halfLength,
			};
			break;
		case SOUTH:
			points = new float[]{
				x,          y + halfLength,
				x - halfLength, y - halfLength,
				x + halfLength, y - halfLength, 
			};
			break;
		case NORTH_WEST:
			points = new float[]{
				x - halfLength, y - halfLength,
				x + halfLength, y - halfLength,
				x - halfLength, y + halfLength,
			};
			break;
		case NORTH_EAST:
			points = new float[]{
				x + halfLength, y - halfLength,
				x + halfLength, y + halfLength,
				x - halfLength, y - halfLength,
			};
			break;
		case SOUTH_EAST:
			points = new float[]{
				x + halfLength, y + halfLength,
				x - halfLength, y + halfLength,
				x + halfLength, y - halfLength,
			};
			break;
		case SOUTH_WEST:
			points = new float[]{
				x - halfLength, y - halfLength,
				x + halfLength, y + halfLength,
				x - halfLength, y + halfLength,
			};
			break;
		}
		Shape shape = new Polygon(points);
		graphics.fill(shape);
	}
	
	
	/**
	 * Draws a selection rectangle to it's required place.
	 */
	private void drawSelection() {
		if(this._selection._x >= this._screen._x && 
		   this._selection._x <= this._screen._x + this._screen._width - 1 &&
		   this._selection._y >= this._screen._y &&
		   this._selection._y <= this._screen._y + this._screen._height - 1)
			this._imgSelection.draw(
				this._screen.mapXToScreenX(this._selection._x),
				this._screen.mapYToScreenY(this._selection._y),
				GraphicPlayState.SCALE
			);
	}
	
	
	/**
	 * Is the selected position currently visible?
	 * 
	 * @param position 
	 * @return true if the position is visible.
	 */
 	private boolean isInScreen(Position position) {
 		if(position.getX() <  this._screen._x || 
 		   position.getX() >= this._screen._x + this._screen._width ||
 		   position.getY() <  this._screen._y ||
 		   position.getY() >= this._screen._y + this._screen._height)
 			return false;
 		return true;
 	}
	
 	
}
